#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QDebug>
#include <QTextCodec>
#include <QDesktopServices>
#include <QDir>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QDateTime>
#include <QModelIndex>
#include <QSettings>
#include <QVariant>
#include <QTextDocument>
#include <QtConcurrentRun>
#include <QFutureWatcher>
#include <QThreadPool>
#include <QClipboard>
#include <QApplication>
#include <QTextCodec>
#include <QtEndian>
#include <QFontDatabase>
#include <qmdisplaystate.h>
#define USE_ENCA 1
#include "enca.h"
#if USE_ENCA
#else
#include "iconv.h"
#endif
#include "rengine.h"
#include "rfilemodel.h"
#include "rconstdifinition.h"
#include "rrecentfilemodel.h"
#include "rbookmark.h"
#include "rtxtfilemodel.h"
#include "rumdfilemodel.h"

//id, name, default value
const QVariant REngine::settingsMap[][4] = {
    {REngine::TextColor, KSettingTextColor, KSettingTextColorDefault, KTypeStr},
    {REngine::TextSmooth, KSettingTextSmooth, KSettingTextSmoothDefault, KTypeBool},
    {REngine::TextLineSpace, KSettingTextLineSpace, KSettingTextLineSpaceDefault, KTypeInt},
    {REngine::TextBackgroud, KSettingTextBackgroud, KSettingTextBackgroudDefault, KTypeStr},
    {REngine::TextFontBold, KSettingTextFontBold, KSettingTextFontBoldDefault, KTypeBool},
    {REngine::TextFontFamiliy, KSettingTextFontFamiliy, KSettingTextFontFamiliyDefault, KTypeStr},
    {REngine::TextFontItalic, KSettingTextFontItalic, KSettingTextFontItalicDefault, KTypeBool},
    {REngine::TextFontLetterSpacing, KSettingTextFontLetterSpacing, KSettingTextFontLetterSpacingDefault, KTypeInt},
    {REngine::TextFontPixelSize, KSettingTextFontPixelSize, KSettingTextFontPixelSizeDefault, KTypeInt},
    {REngine::TextFontPointSize, KSettingTextFontPointSize, KSettingTextFontPointSizeDefault, KTypeInt},
    {REngine::TextFontStrikeout, KSettingTextFontStrikeout, KSettingTextFontStrikeoutDefault, KTypeBool},
    {REngine::TextFontUnderline, KSettingTextFontUnderline, KSettingTextFontUnderlineDefault, KTypeBool},
    {REngine::TextFontWeight, KSettingTextFontWeight, KSettingTextFontWeightDefault, KTypeInt},
    {REngine::TextFontWordSpacing, KSettingTextFontWordSpacing, KSettingTextFontWordSpacingDefault, KTypeInt},
    {REngine::TextIgnoreEmptyLine, KSettingTextIgnoreEmptyLine, KSettingTextIgnoreEmptyLineDefault, KTypeBool},
    {REngine::Orientation, KSettingGeneralOrientation, KSettingGeneralOrientationDefault, KTypeInt},
    {REngine::Codec, KSettingGeneralCodec, KSettingGeneralCodecDefault, KTypeStr},
    {REngine::ShowToolbar, KSettingGeneralShowToolbar, KSettingGeneralShowToolbarDefault, KTypeBool},
    {REngine::LastReadPath, KSettingGeneralLastReadPath, KSettingGeneralLastReadPathDefault, KTypeStr},
    {REngine::ShowQuickSetting, KSettingGeneralShowQuickSetting, KSettingGeneralShowQuickSettingDefault, KTypeBool},
    {REngine::AutoPageTurning, KSettingGeneralAutoPageTurning, KSettingGeneralAutoPageTurningDefault, KTypeBool},
    {REngine::AutoPageTurningTimeOut, KSettingGeneralAutoPageTurningTimeOut, KSettingGeneralAutoPageTurningTimeOutDefault, KTypeInt},
    {REngine::ShowAutoPageTurningTime, KSettingGeneralShowAutoPageTurningTime, KSettingGeneralShowAutoPageTurningTimeDefault, KTypeBool},
    {REngine::DisableDisplayBlank, KSettingGeneralDisableDisplayBlank, KSettingGeneralDisableDisplayBlankDefault, KTypeBool},
    {REngine::NightMode, KSettingGeneralNightMode, KSettingGeneralNightModeDefault, KTypeBool},
    {QVariant(), QVariant(), QVariant()}
};

//Language id, language code, default codec, chapter key
const QVariant REngine::languageCodecMap[][3] = {
    {QLocale::C, "en", KCodecUTF8},
    {QLocale::Abkhazian, "ab", KCodecUTF8},
    {QLocale::Afan, "", KCodecUTF8},
    {QLocale::Afar, "aa", KCodecUTF8},
    {QLocale::Afrikaans, "af", KCodecUTF8},
    {QLocale::Albanian, "sq", KCodecISO_8859_16},
    {QLocale::Amharic, "am", KCodecUTF8},
    {QLocale::Arabic, "ar", KLanRegionArabicDefaultCodec},
    {QLocale::Armenian, "hy", KCodecUTF8},
    {QLocale::Assamese, "as", KCodecUTF8},
    {QLocale::Aymara, "ay", KCodecUTF8},
    {QLocale::Azerbaijani, "az", KCodecUTF8},
    {QLocale::Bashkir, "ba", KCodecUTF8},
    {QLocale::Basque, "eu", KCodecUTF8},
    {QLocale::Bengali, "bn", KCodecUTF8},
    {QLocale::Bhutani, "", KCodecUTF8},
    {QLocale::Bihari, "bh", KCodecUTF8},
    {QLocale::Bislama, "bi", KCodecUTF8},
    {QLocale::Bosnian, "bs", KCodecUTF8},
    {QLocale::Breton, "br", KCodecUTF8},
    {QLocale::Bulgarian, "bg", KCodecUTF8},
    {QLocale::Burmese, "my", KCodecUTF8},
    {QLocale::Byelorussian, "", KCodecUTF8},
    {QLocale::Cambodian, "", KCodecUTF8},
    {QLocale::Catalan, "ca", KCodecUTF8},
    {QLocale::Chinese, "zh", KCodecUTF8},
    {QLocale::Cornish, "kw", KCodecUTF8},
    {QLocale::Corsican, "co", KCodecUTF8},
    {QLocale::Croatian, "hr", KCodecISO_8859_16},
    {QLocale::Czech, "cs", KCodecUTF8},
    {QLocale::Danish, "da", KCodecUTF8},
    {QLocale::Divehi, "dv", KCodecUTF8},
    {QLocale::Dutch, "nl", KCodecUTF8},
    {QLocale::English, "en", KCodecUTF8},
    {QLocale::Esperanto, "eo", KCodecISO_8859_3},
    {QLocale::Estonian, "et", KLanRegionBalticDefaultCodec},
    {QLocale::Faroese, "fo", KCodecUTF8},
    {QLocale::FijiLanguage, "", KCodecUTF8},
    {QLocale::Finnish, "fi", KCodecUTF8},
    {QLocale::French, "fr", KCodecISO_8859_16},
    {QLocale::Frisian, "", KCodecUTF8},
    {QLocale::Gaelic, "gd", KCodecUTF8},
    {QLocale::Galician, "gl", KCodecUTF8},
    {QLocale::Georgian, "ka", KCodecUTF8},
    {QLocale::German, "de", KCodecISO_8859_16},
    {QLocale::Greek, "el", KLanRegionGreekDefaultCodec},
    {QLocale::Greenlandic, "", KCodecUTF8},
    {QLocale::Guarani, "gn", KCodecUTF8},
    {QLocale::Gujarati, "gu", KCodecUTF8},
    {QLocale::Hausa, "ha", KCodecUTF8},
    {QLocale::Hebrew, "he", KLanRegionHebrewDefaultCodec},
    {QLocale::Hindi, "hi", KCodecUTF8},
    {QLocale::Hungarian, "hu", KCodecISO_8859_16},
    {QLocale::Icelandic, "is", KCodecISO_8859_10},
    {QLocale::Indonesian, "id", KCodecUTF8},
    {QLocale::Interlingua, "ia", KCodecUTF8},
    {QLocale::Interlingue, "ie", KCodecUTF8},
    {QLocale::Inuktitut, "iu", KCodecUTF8},
    {QLocale::Inupiak, "", KCodecUTF8},
    {QLocale::Irish, "ga", KCodecISO_8859_14},
    {QLocale::Italian, "it", KCodecISO_8859_16},
    {QLocale::Japanese, "ja", KLanRegionJapanesDefaultCodec},
    {QLocale::Javanese, "jv", KCodecUTF8},
    {QLocale::Kannada, "kn", KCodecUTF8},
    {QLocale::Kashmiri, "ks", KCodecUTF8},
    {QLocale::Kazakh, "kk", KCodecUTF8},
    {QLocale::Kinyarwanda, "rw", KCodecUTF8},
    {QLocale::Kirghiz, "ky", KCodecUTF8},
    {QLocale::Korean, "ko", KLanRegionKoreanDefaultCodec},
    {QLocale::Kurdish, "ku", KCodecUTF8},
    {QLocale::Kurundi, "", KCodecUTF8},
    {QLocale::Laothian, "", KCodecUTF8},
    {QLocale::Latin, "la", KCodecUTF8},
    {QLocale::Latvian, "lv", KLanRegionBalticDefaultCodec},
    {QLocale::Lingala, "ln", KCodecUTF8},
    {QLocale::Lithuanian, "lt", KLanRegionBalticDefaultCodec},
    {QLocale::Macedonian, "mk", KCodecUTF8},
    {QLocale::Malagasy, "mg", KCodecUTF8},
    {QLocale::Malay, "ms", KCodecUTF8},
    {QLocale::Malayalam, "ml", KCodecUTF8},
    {QLocale::Maltese, "mt", KCodecISO_8859_3},
    {QLocale::Manx, "gv", KCodecUTF8},
    {QLocale::Maori, "mi", KCodecUTF8},
    {QLocale::Marathi, "mr", KCodecUTF8},
    {QLocale::Moldavian, "", KCodecUTF8},
    {QLocale::Mongolian, "mn", KCodecUTF8},
    {QLocale::NauruLanguage, "", KCodecUTF8},
    {QLocale::Nepali, "ne", KCodecUTF8},
    {QLocale::Norwegian, "nn", KCodecUTF8},
    {QLocale::NorwegianBokmal, "", KCodecUTF8},
    {QLocale::Nynorsk, "", KCodecUTF8},
    {QLocale::NorwegianNynorsk, "", KCodecUTF8},
    {QLocale::Occitan, "oc", KCodecUTF8},
    {QLocale::Oriya, "or", KCodecUTF8},
    {QLocale::Pashto, "", KCodecUTF8},
    {QLocale::Persian, "fa", KCodecUTF8},
    {QLocale::Polish, "pl", KCodecISO_8859_13},
    {QLocale::Portuguese, "pt", KCodecUTF8},
    {QLocale::Punjabi, "", KCodecUTF8},
    {QLocale::Quechua, "qu", KCodecUTF8},
    {QLocale::RhaetoRomance, "", KCodecUTF8},
    {QLocale::Romanian, "ro", KCodecISO_8859_16},
    {QLocale::Russian, "ru", KLanRegionCyrillicDefaultCodec},
    {QLocale::Samoan, "sm", KCodecUTF8},
    {QLocale::Sangho, "", KCodecUTF8},
    {QLocale::Sanskrit, "sa", KCodecUTF8},
    {QLocale::Serbian, "sr", KCodecISO_8859_16},
    {QLocale::SerboCroatian, "", KCodecUTF8},
    {QLocale::Sesotho, "", KCodecUTF8},
    {QLocale::Setswana, "", KCodecUTF8},
    {QLocale::Shona, "sn", KCodecUTF8},
    {QLocale::Sindhi, "sd", KCodecUTF8},
    {QLocale::Singhalese, "", KCodecUTF8},
    {QLocale::Siswati, "", KCodecUTF8},
    {QLocale::Slovak, "sk", KCodecUTF8},
    {QLocale::Slovenian, "sl", KCodecISO_8859_16},
    {QLocale::Somali, "so", KCodecUTF8},
    {QLocale::Spanish, "es", KCodecUTF8},
    {QLocale::Sundanese, "su", KCodecUTF8},
    {QLocale::Swahili, "sw", KCodecUTF8},
    {QLocale::Swedish, "sv", KCodecUTF8},
    {QLocale::Tagalog, "tl", KCodecUTF8},
    {QLocale::Tajik, "tg", KCodecUTF8},
    {QLocale::Tamil, "ta", KCodecUTF8},
    {QLocale::Tatar, "tt", KCodecUTF8},
    {QLocale::Telugu, "te", KCodecUTF8},
    {QLocale::Thai, "th", KLanRegionThaiDefaultCodec},
    {QLocale::Tibetan, "bo", KCodecUTF8},
    {QLocale::Tigrinya, "ti", KCodecUTF8},
    {QLocale::TongaLanguage, "", KCodecUTF8},
    {QLocale::Tsonga, "ts", KCodecUTF8},
    {QLocale::Turkish, "tr", KLanRegionTurkishDefaultCodec},
    {QLocale::Turkmen, "tk", KCodecUTF8},
    {QLocale::Twi, "tw", KCodecUTF8},
    {QLocale::Uigur, "", KCodecUTF8},
    {QLocale::Ukrainian, "uk", KLanRegionCyrillicDefaultCodec},
    {QLocale::Urdu, "ur", KCodecUTF8},
    {QLocale::Uzbek, "uz", KCodecUTF8},
    {QLocale::Vietnamese, "vi", KLanRegionVietnameseDefaultCodec},
    {QLocale::Volapuk, "", KCodecUTF8},
    {QLocale::Welsh, "cy", KCodecISO_8859_14},
    {QLocale::Wolof, "wo", KCodecUTF8},
    {QLocale::Xhosa, "xh", KCodecUTF8},
    {QLocale::Yiddish, "yi", KCodecUTF8},
    {QLocale::Yoruba, "yo", KCodecUTF8},
    {QLocale::Zhuang, "za", KCodecUTF8},
    {QLocale::Zulu, "zu", KCodecUTF8},
    {QLocale::Bosnian, "bs", KCodecUTF8},
    {QLocale::Divehi, "dv", KCodecUTF8},
    {QLocale::Manx, "gv", KCodecUTF8},
    {QLocale::Cornish, "kw", KCodecUTF8},
    {QLocale::Akan, "ak", KCodecUTF8},
    {QLocale::Konkani, "", KCodecUTF8},
    {QLocale::Ga, "", KCodecUTF8},
    {QLocale::Igbo, "ig", KCodecUTF8},
    {QLocale::Kamba, "", KCodecUTF8},
    {QLocale::Syriac, "", KCodecUTF8},
    {QLocale::Blin, "", KCodecUTF8},
    {QLocale::Geez, "", KCodecUTF8},
    {QLocale::Koro, "", KCodecUTF8},
    {QLocale::Sidamo, "", KCodecUTF8},
    {QLocale::Atsam, "", KCodecUTF8},
    {QLocale::Tigre, "", KCodecUTF8},
    {QLocale::Jju, "", KCodecUTF8},
    {QLocale::Friulian, "", KCodecUTF8},
    {QLocale::Venda, "ve", KCodecUTF8},
    {QLocale::Ewe, "ee", KCodecUTF8},
    {QLocale::Walamo, "", KCodecUTF8},
    {QLocale::Hawaiian, "", KCodecUTF8},
    {QLocale::Tyap, "", KCodecUTF8},
    {QLocale::Chewa, "", KCodecUTF8},
    {QLocale::Filipino, "", KCodecUTF8},
    {QLocale::SwissGerman, "", KCodecUTF8},
    {QLocale::SichuanYi, "", KCodecUTF8},
    {QLocale::Kpelle, "", KCodecUTF8},
    {QLocale::LowGerman, "", KCodecUTF8},
    {QLocale::SouthNdebele, "", KCodecUTF8},
    {QLocale::NorthernSotho, "", KCodecUTF8},
    {QLocale::NorthernSami, "", KCodecUTF8},
    {QLocale::Taroko, "", KCodecUTF8},
    {QLocale::Gusii, "", KCodecUTF8},
    {QLocale::Taita, "", KCodecUTF8},
    {QLocale::Fulah, "ff", KCodecUTF8},
    {QLocale::Kikuyu, "ki", KCodecUTF8},
    {QLocale::Samburu, "", KCodecUTF8},
    {QLocale::Sena, "", KCodecUTF8},
    {QLocale::NorthNdebele, "", KCodecUTF8},
    {QLocale::Rombo, "", KCodecUTF8},
    {QLocale::Tachelhit, "", KCodecUTF8},
    {QLocale::Kabyle, "", KCodecUTF8},
    {QLocale::Nyankole, "", KCodecUTF8},
    {QLocale::Bena, "", KCodecUTF8},
    {QLocale::Vunjo, "", KCodecUTF8},
    {QLocale::Bambara, "bm", KCodecUTF8},
    {QLocale::Embu, "", KCodecUTF8},
    {QLocale::Cherokee, "", KCodecUTF8},
    {QLocale::Morisyen, "", KCodecUTF8},
    {QLocale::Makonde, "", KCodecUTF8},
    {QLocale::Langi, "", KCodecUTF8},
    {QLocale::Ganda, "lg", KCodecUTF8},
    {QLocale::Bemba, "", KCodecUTF8},
    {QLocale::Kabuverdianu, "", KCodecUTF8},
    {QLocale::Meru, "", KCodecUTF8},
    {QLocale::Kalenjin, "", KCodecUTF8},
    {QLocale::Nama, "", KCodecUTF8},
    {QLocale::Machame, "", KCodecUTF8},
    {QLocale::Colognian, "", KCodecUTF8},
    {QLocale::Masai, "", KCodecUTF8},
    {QLocale::Soga, "", KCodecUTF8},
    {QLocale::Luyia, "", KCodecUTF8},
    {QLocale::Asu, "", KCodecUTF8},
    {QLocale::Teso, "", KCodecUTF8},
    {QLocale::Saho, "", KCodecUTF8},
    {QLocale::KoyraChiini, "", KCodecUTF8},
    {QLocale::Rwa, "", KCodecUTF8},
    {QLocale::Luo, "", KCodecUTF8},
    {QLocale::Chiga, "", KCodecUTF8},
    {QLocale::CentralMoroccoTamazight, "", KCodecUTF8},
    {QLocale::KoyraboroSenni, "", KCodecUTF8},
    {QLocale::Shambala, "", KCodecUTF8},
    {0, "", KCodecNotAvailable}
};

//enca codec, Qt codec
const QVariant REngine::enca2QtCodecMap[][2] = {
    //Chinese
    {"gbk", KCodecGBK},
    {"big5", KCodecBig5},
    {"hz", KCodecNotAvailable},

    //ukrainian
    {"cp1251", KCodecWindows1251},
    {"ibm855", KCodecNotAvailable},
    {"iso88595", KCodecISO_8859_5},
    {"cp1125", KCodecNotAvailable},
    {"koi8u", KCodecKOI8_U},
    {"maccyr", KCodecNotAvailable},

    //slovene
    {"iso88592",   KCodecISO_8859_2},
    {"cp1250",     KCodecWindows1250},
    {"ibm852",     KCodecNotAvailable},
    {"macce",      KCodecNotAvailable},
    {"cork",       KCodecNotAvailable},

    //slovak
    {"cp1250",     KCodecWindows1250},
    {"iso88592",   KCodecISO_8859_2},
    {"ibm852",     KCodecNotAvailable},
    {"keybcs2",    KCodecNotAvailable},
    {"macce",      KCodecNotAvailable},
    {"koi8cs2",    KCodecCS_KOI8R},
    {"cork",       KCodecNotAvailable},

    //russian
    {"koi8r",      KCodecKOI8_R},
    {"cp1251",     KCodecWindows1251},
    {"iso88595",   KCodecISO_8859_5},
    {"ibm866",     KCodecIBM_866},
    {"maccyr",     KCodecNotAvailable},

    //polish
    {"iso88592",   KCodecISO_8859_2},
    {"cp1250",     KCodecWindows1250},
    {"ibm852",     KCodecNotAvailable},
    {"macce",      KCodecNotAvailable},
    {"iso885913",  KCodecISO_8859_3},
    {"iso885916",  KCodecISO_8859_6},
    {"baltic",     KCodecNotAvailable},
    {"cork",       KCodecNotAvailable},

    //latvian
    {"cp1257",     KCodecWindows1257},
    {"iso88594",   KCodecISO_8859_4},
    {"ibm775",     KCodecNotAvailable},
    {"iso885913",  KCodecISO_8859_13},
    {"macce",      KCodecNotAvailable},
    {"baltic",     KCodecNotAvailable},

    //lithuanian
    {"cp1257",     KCodecWindows1257},
    {"iso88594",   KCodecISO_8859_4},
    {"ibm775",     KCodecNotAvailable},
    {"iso885913",  KCodecISO_8859_13},
    {"macce",      KCodecNotAvailable},
    {"baltic",     KCodecNotAvailable},

    //hungarian
    {"iso88592",   KCodecISO_8859_2},
    {"cp1250",     KCodecWindows1250},
    {"ibm852",     KCodecNotAvailable},
    {"macce",      KCodecNotAvailable},
    {"cork",       KCodecNotAvailable},

    //croatian
    {"cp1250",     KCodecWindows1250},
    {"iso88592",   KCodecISO_8859_2},
    {"ibm852",     KCodecNotAvailable},
    {"macce",      KCodecNotAvailable},
    {"cork",       KCodecNotAvailable},

    //estonian
    {"iso88594",   KCodecISO_8859_4},
    {"cp1257",     KCodecWindows1257},
    {"ibm775",     KCodecNotAvailable},
    {"iso885913",  KCodecISO_8859_13},
    {"macce",      KCodecNotAvailable},
    {"baltic",     KCodecNotAvailable},

    //czech
    {"iso88594",   KCodecISO_8859_4},
    {"cp1257",     KCodecWindows1257},
    {"ibm775",     KCodecNotAvailable},
    {"iso885913",  KCodecISO_8859_13},
    {"macce",      KCodecNotAvailable},
    {"baltic",     KCodecNotAvailable},

    //bulgarian
    {"cp1251",     KCodecWindows1251},
    {"iso88595",   KCodecISO_8859_5},
    {"ibm855",     KCodecNotAvailable},
    {"maccyr",     KCodecNotAvailable},
    {"ecma113",    KCodecNotAvailable},

    //belarussian
    {"cp1251",     KCodecWindows1251},
    {"ibm866",     KCodecNotAvailable},
    {"iso88595",   KCodecISO_8859_5},
    {"koi8uni",    KCodecNotAvailable},
    {"maccyr",     KCodecNotAvailable},
    {"ibm855",     KCodecNotAvailable},
    {"koi8u",      KCodecKOI8_U},

    {"", KCodecNotAvailable}

};

REngine::REngine(QObject *parent) :
    QObject(parent)
{
    initDb();
    loadSettings();
    //load setting for display blank
//    setDisableDisplayBlank(setting(REngine::DisableDisplayBlank).toBool());
    connect(&m_open_file_watcher, SIGNAL(finished()), this, SLOT(openFileFinished()));
    connect(&m_get_chapter_watcher, SIGNAL(finished()), this, SLOT(getChapterFinished()));
}

REngine::~REngine() {
    saveSettings();
}

RFileModel* REngine::fileModel(QString fileName) {
    if (m_fileName_model_map.contains(fileName)) {
        //Already opened, need to reset the result to success
        m_fileName_model_map[fileName]->setOpenFileResult(OpenFileSuccess);
        return m_fileName_model_map[fileName];
    }
    else {
        QFileInfo fileInfo(fileName);
        RFileModel* model = NULL;
        QString fileSuffix = fileInfo.suffix().toLower();
        if (fileSuffix == KTxtFileSuffix) {
            //txt file, use RTxtFileModel
            qDebug()<<"Creatae RTxtFileModel for txt file";
            model = new RTxtFileModel(*this);
        }
        else if (fileSuffix == KUmdFileSuffix) {
            //Umd file, use RUmdFileModel
            qDebug()<<"Create RUmdFileModel for UMD file";
            model = new RUmdFileModel(*this);
        }
        else {
            qDebug()<<"Unsupported file format:"<<fileSuffix;
        }

        if (model) {
            if (model->setFile(fileName)) {
                m_fileName_model_map[fileName] = model;
            }
            else {
                delete model;
                model = NULL;
            }
        }
        return model;
    }
}

QObject* REngine::recentFileModel() {
    return new RRecentFileModel(this);
}

bool REngine::fileLinePosition(const QString& fileName,
                               QHash<int, qint64>& linePosition,
                               QList<int>& chapterLines) {
    QString sql;
    bool found = false;
    qint64 dbLastModifed = 0;
    qint64 fileLastModified = 0;
    QFileInfo fileInfo(fileName);
    fileLastModified = fileInfo.lastModified().toMSecsSinceEpoch();
    linePosition.clear();
    chapterLines.clear();
    sql.append(KSelect +KFileLastModifyTime
               +KFrom + KTableFile
               +KWhere + KFileName + KEqual + KSingleQuotes + fileName +KSingleQuotes);
    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        if (sqlQuery->next()) {
            dbLastModifed = sqlQuery->value(sqlQuery->record().indexOf(KFileLastModifyTime)).toLongLong();
            qDebug()<<"file "<<fileName<<"last modifytime"<<dbLastModifed;
        }
        else {
            qDebug()<<fileName<<"is not stored in DB";
            showAllInFileTable();
        }
        delete sqlQuery;
    }
    //File is not modified, just read from the line_position file
    if (dbLastModifed == fileLastModified) {
        qDebug()<<"last modify times are the same, read from line position file";
        found = readLinePosition(linePositionFileName(fileName), linePosition, chapterLines);
    }
    return found;
}

void REngine::saveFileLinePosition(const QString& fileName,
                                   const QHash<int, qint64>& linePosition,
                                   const QList<int>& chapterLine) {
    int id = fileId(fileName);
    if (id == KInvalidId) {
        qDebug()<<"file not stored, storing";
        id = addFileToDb(fileName, linePosition.count());
    }
    if (id != KInvalidId) {
        QByteArray linePositionStr;
        int lineNum = 0;
        for (QHash<int, qint64>::const_iterator iterator=linePosition.begin();
             iterator != linePosition.end();
             iterator++, lineNum++) {
            linePositionStr += QString::number(iterator.value());

            //If line is chapter, add chapter mark
            if (chapterLine.contains(lineNum)) {
                linePositionStr += KChapterMark;
            }

            linePositionStr += KNewLine;
        }
        QFile file(linePositionFileName(fileName));
        if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            file.write(linePositionStr);
            file.flush();
            file.close();
        }
        else {
            qDebug()<<"ERROR::Open linePositionFile "<<linePositionFileName(fileName)<<" failed";
        }
    }
}

bool REngine::readLinePosition(const QString& linePositionFileName,
                               QHash<int, qint64>& linePositions,
                               QList<int>& chapterLines) {
    bool success = false;
    linePositions.clear();
    chapterLines.clear();
    QFile file(linePositionFileName);
    if (file.open(QIODevice::ReadOnly)) {
        qint64 position;
        QString linePositionStr = file.readAll();
        QStringList linePositionList = linePositionStr.split(KNewLine, QString::SkipEmptyParts);
        for (int i=0; i < linePositionList.count(); i++) {
            int markIndex = linePositionList[i].indexOf(KChapterMark);
            if (markIndex == -1) {
                //not chapter
                position = linePositionList[i].toLongLong();
            }
            else {
                //chapter, need to delete chapter mark
                position = linePositionList[i].left(markIndex).toLongLong();
                chapterLines.append(i);
            }
            linePositions[i] = position;
        }
        if (linePositions.count()) {
            success = true;
        }
        qDebug()<<"line position count"<<linePositions.count();
    }
    else {
        qDebug()<<"ERROR::Could not open line position file:"<<linePositionFileName;
    }
    return success;
}

int REngine::addFileToDb(const QString& fileName, int lineCount) {
    QFileInfo fileInfo(fileName);
    int fileId = KInvalidId;
    if (!fileInfo.exists()) {
        qDebug()<<fileName<<"does not exist";
        return false;
    }

    qint64 modifiedTime = fileInfo.lastModified().toMSecsSinceEpoch();

    QString sql;
    InsertUpdateHelper *helper = new InsertUpdateHelper(KTableFile);
    helper->setId(KFileId, KInvalidId);
    helper->addValue(KFileName, fileName);
    helper->addValue(KFileLineCount, lineCount);
    helper->addValue(KFileLastReadTime, QDateTime::currentMSecsSinceEpoch());
    helper->addValue(KFileLastModifyTime, modifiedTime);
    helper->addValue(KFileLastReadLine, 0);
    helper->addValue(KFileLastReadCharIndex, 0);
    helper->addValue(KFileCodec, KCodecAuto);
    helper->insertSql(sql);

    QSqlQuery *sqlquery = query(sql, true);
    if (sqlquery) {
        fileId = sqlquery->lastInsertId().toInt();
        qDebug()<<"add file "<<fileName<<"success, id:"<<fileId;
        delete sqlquery;
        showAllInFileTable();
    }
    else {
        qDebug()<<"ERROR::add file "<<fileName<<" to db failed";
    }
    delete helper;
    return fileId;
}

bool REngine::fileExistInDb(const QString& fileName) {
    return fileId(fileName) != KInvalidId;
}

int REngine::fileId(const QString& fileName) {
    QString sql;
    int fileId = KInvalidId;
    showAllInFileTable();
    sql.append(KSelect + KFileId
               +KFrom + KTableFile
               +KWhere + KFileName + KEqual + KSingleQuotes + fileName +KSingleQuotes);
    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        if (sqlQuery->next()) {
            fileId = sqlQuery->value(sqlQuery->record().indexOf(KFileId)).toInt();
        }
        delete sqlQuery;
    }
    return fileId;
}

QSqlQuery *REngine::query(QString &sqlStr, bool /*select*/) {
    QSqlQuery *query = new QSqlQuery();
    if (query->exec(sqlStr)) {
        qDebug()<<"SQL:"<<sqlStr<<" successed";
        //        commitDb();
        return query;
    }
    else {
        qDebug()<<"ERROR::sql query:"<<sqlStr<<"execute failed"<<query->lastError();
        delete query;
        return NULL;
    }
}

bool REngine::initStorageLocation() {
    QString storageLocation = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
    qDebug()<<"storageLocation:"<<storageLocation;
    QDir dir(storageLocation);
    if (!dir.exists()) {
        return dir.mkpath(storageLocation);
    }
    //    dir.remove(DatabaseName());
    return true;
}

QString REngine::storageLocation() {
    return QDesktopServices::storageLocation(QDesktopServices::DataLocation);
}

QString REngine::linePositionFileName(const QString& fileName) {
    QFileInfo fileInfo(fileName);
    QString path = QDir::cleanPath(fileInfo.absoluteFilePath());
    path.replace(KPathSeparator, KUnderLine);
    path.replace(KPathSeparatorWin, KUnderLine);
    path.replace(KDriveSymbol, KUnderLine);
    return QDir::cleanPath(storageLocation()+KPathSeparator+path);
}

QString REngine::settingFileName() {
    return QDir::cleanPath(storageLocation()+KPathSeparator+KSettingFileName);
}

QString REngine::DatabaseName() {
#ifdef MEM_DB
    return KDatabaseName;
#else
    return storageLocation() + KPathSeparator + KDatabaseName;
#endif
}

bool REngine::initDb() {
    if (!initStorageLocation()) {
        qDebug()<<"initStorageLocation failed";
        return false;
    }
    QSqlDatabase db = QSqlDatabase::addDatabase(KDatabaseType);
    db.setDatabaseName(DatabaseName());
    if (!db.open()) {
        qDebug()<<tr("Cannot open database")
               <<tr("Unable to establish a database connection.\n"
                    "This example needs SQLite support. Please read "
                    "the Qt SQL driver documentation for information how "
                    "to build it.\n\n"
                    "Click Cancel to exit.");
        return false;
    }
    createFileTable();
    createBookmarkTable();
    return true;
}

bool REngine::createFileTable() {
    QString sql;
    sql.append(KCreateTable + KTableFile + "(");
    sql.append(KFileId + KTypeInt + KPrimaryKey + KAutoIncrement);
    sql.append(KComma + KFileName + KTypeStr);
    sql.append(KComma + KFileLineCount + KTypeInt);
    sql.append(KComma + KFileLastModifyTime + KTypeInt);
    sql.append(KComma + KFileLastReadTime + KTypeInt);
    sql.append(KComma + KFileLastReadLine + KTypeInt);
    sql.append(KComma + KFileLastReadCharIndex + KTypeInt);
    sql.append(KComma + KFileCodec + KTypeStr);
    sql.append(+")");
    return createTable(sql);
}

bool REngine::createBookmarkTable() {
    QString sql;
    sql.append(KCreateTable + KTableBookmark + "(");
    sql.append(KBookmarkId + KTypeInt + KPrimaryKey + KAutoIncrement);
    sql.append(KComma + KBookmarkFileId + KTypeInt);
    sql.append(KComma + KBookmarkLine + KTypeInt);
    sql.append(KComma + KBookmarkCharIndex + KTypeInt);
    sql.append(KComma + KBookmarkAddTime + KTypeInt);
    sql.append(KComma + KBookmarkNote + KTypeStr);
    sql.append(+")");
    return createTable(sql);
}



bool REngine::createTable(QString &sql) {
    QSqlQuery query;
    bool result = query.exec(sql);
    qDebug()<<"result:"<<result<<"CreateTable:"<<sql<<"last error"<<query.lastError();
    return result;
}


REngine::InsertUpdateHelper::InsertUpdateHelper(const QString &tableName) {
    m_table_name = tableName;
}

void REngine::InsertUpdateHelper::setTable(const QString &tableName) {
    m_table_name = tableName;
}

bool REngine::InsertUpdateHelper::insertSql(QString &sqlString) {
    if (m_table_name.isEmpty() || m_column_value.isEmpty()) {
        qDebug()<<"data is not valid";
        return false;
    }
    sqlString = KInsert + m_table_name + "(";
    for (QMap<QString, QString>::iterator i=m_column_value.begin(); i!=m_column_value.end(); i++) {
        sqlString += i.key();
        if (i != --m_column_value.end()) {
            sqlString += KComma;
        }
    }
    sqlString += QString(")")
            + KValues + "(";
    for (QMap<QString, QString>::iterator i=m_column_value.begin(); i!=m_column_value.end(); i++) {
        sqlString += i.value();
        if (i != --m_column_value.end()) {
            sqlString += KComma;
        }
    }
    sqlString += QString(")");
    qDebug()<<"insterHelper::insert sql string:"<<sqlString;
    return true;
}

bool REngine::InsertUpdateHelper::updateSql(QString &sqlString) {
    if (m_table_name.isEmpty() || m_column_value.isEmpty() ||
            m_where.isEmpty()) {
        qDebug()<<"data is not valid";
        return false;
    }
    sqlString = KUpdate + m_table_name + KSet;
    for (QMap<QString, QString>::iterator i=m_column_value.begin(); i!=m_column_value.end(); i++) {
        sqlString += i.key() + KEqual + i.value();
        if (i != --m_column_value.end()) {
            sqlString += KComma;
        }
    }

    sqlString += KWhere + m_where;
    qDebug()<<"insterHelper::udpate sql string:"<<sqlString;
    return true;
}

void REngine::InsertUpdateHelper::addNull(const QString &column) {
    m_column_value[column] = KNull;
}

void REngine::InsertUpdateHelper::addValue(const QString &column, const QString &value) {
    m_column_value[column] = QString("'") + value + QString("'");
}

void REngine::InsertUpdateHelper::addValue(const QString &column, const int &value) {
    m_column_value[column] = QString::number(value);
}

void REngine::InsertUpdateHelper::addValue(const QString &column, const qint64 &value) {
    m_column_value[column] = QString::number(value);
}

void REngine::InsertUpdateHelper::addValue(const QString &column, const bool &value) {
    m_column_value[column] = QString::number(value);
}

void REngine::InsertUpdateHelper::addValue(const QString &column, const double &value) {
    m_column_value[column] = QString::number(value);
}

void REngine::InsertUpdateHelper::setId(const QString& idColumn, int id) {
    if (id > 0) {
        m_where = idColumn + KEqual + QString::number(id);
        addValue(idColumn, QString::number(id));
    }
    else {
        addNull(idColumn);
    }
}


void REngine::showAllInFileTable() {
    QString sql;
    sql.append(KSelect +KAll
               +KFrom + KTableFile);
    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"file data size"<<sqlQuery->size();
        while (sqlQuery->next()) {
            qDebug()<<"id"<<
                      sqlQuery->value(
                          sqlQuery->record().indexOf(KFileId))
                      .toInt();
            qDebug()<<"name"<<
                      sqlQuery->value(
                          sqlQuery->record().indexOf(KFileName))
                      .toString();
            qDebug()<<"last modifytime"<<
                      sqlQuery->value(
                          sqlQuery->record().indexOf(KFileLastModifyTime))
                      .toLongLong();
        }
        delete sqlQuery;
    }
}

void REngine::removeAllRecentFileInfos() {
    QList<RFileInfo> infoList;
    if (recentFileInfo(infoList)) {
        for (int i = 0; i < infoList.count(); i++) {
            removeRecentFileInfo(infoList[i].name, false);
        }
    }
    emit recentFileInfoChanged();
}

void REngine::removeFileModel(QString fileName) {
    if (m_fileName_model_map.contains(fileName)) {
        RFileModel* model = m_fileName_model_map.value(fileName);
        m_fileName_model_map.remove(fileName);
        delete model;
    }
}

bool REngine::removeRealFile(QString fileName) {
    QFileInfo fileInfo(fileName);
    return fileInfo.dir().remove(fileName);
}

bool REngine::removeFileFromDB(QString fileName) {
    QString sql;
    bool result;
    sql.append(KDelete + KTableFile
               + KWhere + KFileName + KEqual + "'"+fileName+"'");

    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"file deleted";
        result = true;
    }
    else {
        qDebug()<<"file delete failed";
        result = false;
    }
    return result;
}

bool REngine::removeRecentFileInfo(QString fileName, bool notify) {
    //Remove model
    removeFileModel(fileName);

    //Remove line position file
    removeRealFile(linePositionFileName(fileName));

    QFileInfo fileInfo(fileName);
    //Delete uncompressed file
    if (fileInfo.suffix().toLower() == KUmdFileSuffix) {
        removeRealFile(uncompressedContentFileName(fileName));
    }

    //Remove bookmark
    removeAllBookmarks(fileName);

    //Remove file from database
    removeFileFromDB(fileName);
    if (notify) {
        emit recentFileInfoChanged();
    }

    return true;
}


bool REngine::recentFileInfo(QList<RFileInfo>& infoList) {
    QList<RFileInfo> removeList;
    QString sql;
    sql.append(KSelect +KAll
               +KFrom + KTableFile + KOrderBy + KFileLastReadTime + KDesc);

    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"file data size"<<sqlQuery->size();
        while (sqlQuery->next()) {
            RFileInfo info;
            info.id = sqlQuery->value(sqlQuery->record().indexOf(KFileId)).toInt();
            info.name = sqlQuery->value(sqlQuery->record().indexOf(KFileName)).toString();
            info.lineCount = sqlQuery->value(sqlQuery->record().indexOf(KFileLineCount)).toInt();
            info.lastModifyTime = sqlQuery->value(sqlQuery->record().indexOf(KFileLastModifyTime)).toLongLong();
            info.lastReadTime = sqlQuery->value(sqlQuery->record().indexOf(KFileLastReadTime)).toLongLong();
            info.lastReadLine = sqlQuery->value(sqlQuery->record().indexOf(KFileLastReadLine)).toInt();
            info.lastReadCharIndex = sqlQuery->value(sqlQuery->record().indexOf(KFileLastReadCharIndex)).toInt();
            info.codec = sqlQuery->value(sqlQuery->record().indexOf(KFileCodec)).toString();
            QFileInfo fileInfo(info.name);
            //if file is not exist or file has changed, add id to remove list
            if (!fileInfo.exists() || info.lastModifyTime != fileInfo.lastModified().toMSecsSinceEpoch()) {
                qDebug()<<"File"<<info.name<<"does not exist or has been modified, need to be removed";
                //Append remove file id for later DB removal
                removeList.append(info);
            }
            else {
                infoList.append(info);
            }
        }

        delete sqlQuery;
        sqlQuery = NULL;
        //Remove nonexist or modified file
        if (removeList.count()) {
            QString delSql;
            delSql.append(KDelete+KTableFile+
                          KWhere+KFileId+KIn+"(");
            for (int i = 0; i < removeList.count(); i++) {
                //Remove model
                removeFileModel(removeList[i].name);

                //Remove line position file
                removeRealFile(linePositionFileName(removeList[i].name));

                //Remove bookmark
                removeAllBookmarks(removeList[i].name);

                //Append file id for db operation
                if (i != 0) {
                    delSql.append(KComma);
                }
                delSql.append(QString::number(removeList[i].id));
            }
            delSql.append(")");
            sqlQuery = query(delSql);
            if (sqlQuery) {
                qDebug()<<"Delete file success"<<sqlQuery->numRowsAffected();
                delete sqlQuery;
            }
            else {
                qDebug()<<"Delete file failed";
            }

        }
    }
    return true;
}

bool REngine::commitDb() {
    return QSqlDatabase::database().commit();
}


bool REngine::resetModel(QObject* model) {
    RFileModel *fileModel = qobject_cast<RFileModel*>(model);
    if (fileModel) {
        fileModel->reset();
        return true;
    }
    else {
        return false;
    }
}

int REngine::openFile(REngine* engine, QString fileName) {
    RFileModel* model = engine->fileModel(fileName);
    if (model) {
        return model->openFileResult();
    }
    else {
        return OpenFileFailed;
    }
}

void REngine::asyncOpenFile(QString fileName) {
    qDebug()<<"before concurrent"<<QThreadPool::globalInstance()->activeThreadCount();
    qDebug()<<"currentThreadId"<<QThread::currentThreadId();
    QFuture<int> future = QtConcurrent::run(REngine::openFile, this, fileName);
    QFutureWatcher<int> *watcher = new QFutureWatcher<int>();
    watcher->setFuture(future);
    m_open_file_watchers[watcher] = fileName;
    connect(watcher, SIGNAL(finished()), this, SLOT(openFileFinished()));
    qDebug()<<"after concurrent"<<QThreadPool::globalInstance()->activeThreadCount();
}

void REngine::openFileFinished() {
    int result = REngine::OpenFileFailed;
    QString fileName;
    QFutureWatcher<int> *watcher = (QFutureWatcher<int> *)sender();
    if (watcher) {
        result = watcher->future().result();
        if (m_open_file_watchers.contains(watcher)) {
            fileName = m_open_file_watchers[watcher];
            m_open_file_watchers.remove(watcher);
            disconnect(watcher, 0, 0, 0);
            delete watcher;
        }
    }
    emit fileOpened(fileName, result);
}

void REngine::getChapterFinished() {
    QVariant result = m_get_chapter_watcher.future().result();
    emit getChapterResult(result);
}

QString REngine::getLine(QString fileName, int lineNumber) {
    RFileModel* model = fileModel(fileName);
    if (model) {
        QString str = model->data(lineNumber).toString();
#if 0
        if (setting(REngine::TextIgnoreEmptyLine).toBool()) {
            if (!isBlankString(str)) {
                return str;
            }
            else {
                return QString("");
            }
        }
        else {
            return str;
        }
#else
        return str;
#endif
    }
    else {
        qDebug()<<"ERROR::getLine(): file model is not ready";
        return QString();
    }
}

bool REngine::isBlankString(QString string) {
    //Let's assume the blank string's length is not big
    //So we can use char check one by one
    for (int i = 0; i < string.length(); i++) {
        if (!string.at(i).isSpace()) {
            return false;
        }
    }
    return true;
}

int REngine::fileLineNumber(QString fileName) {
    qDebug()<<"fileLineNumber():"<<fileName;
    RFileModel* model = fileModel(fileName);
    if (model) {
        return model->rowCount();
    }
    else {
        qDebug()<<"ERROR::fileLineNumber(): file model is not ready";
        return -1;
    }
}

void REngine::saveLastPosition(QString fileName,
                               int lineNumber,
                               int charIndex,
                               bool saveLastReadTime) {
    QString sql;
    RFileModel* model = fileModel(fileName);
    if (model) {
        int id = model->fileId();
        if (id != KInvalidId) {
            InsertUpdateHelper *helper = new InsertUpdateHelper(KTableFile);
            helper->setId(KFileId, id);
            helper->addValue(KFileLastReadLine, lineNumber);
            helper->addValue(KFileLastReadCharIndex, charIndex);
            if (saveLastReadTime) {
                helper->addValue(KFileLastReadTime, QDateTime::currentMSecsSinceEpoch());
            }
            helper->updateSql(sql);

            QSqlQuery *sqlquery = query(sql, false);
            if (sqlquery) {
                qDebug()<<"REngine::saveLastPosition()"<<
                          "save positon success"<<lineNumber<<charIndex;
                delete sqlquery;
            }
            else {
                qDebug()<<"REngine::saveLastPosition()"<<"ERROR::save positon failed"
                       <<lineNumber<<charIndex;;
            }
            delete helper;
        }
    }
    else {
        qDebug()<<"REngine::saveLastPosition()"<<"ERROR::file model is not ready!";
    }
}

int REngine::lastLineNumber(QString fileName) {
    int linNum = KInvalidId;
    RFileModel* model = fileModel(fileName);
    if (model) {
        QString sql;
        sql.append(KSelect + KFileLastReadLine
                   +KFrom + KTableFile
                   +KWhere + KFileId + KEqual +QString::number(model->fileId()));
        QSqlQuery *sqlQuery = query(sql);
        if (sqlQuery) {
            if (sqlQuery->next()) {
                linNum = sqlQuery->value(sqlQuery->record().indexOf(KFileLastReadLine)).toInt();
            }
            delete sqlQuery;
        }
    }
    else {
        qDebug()<<"REngine::lastLineNumber()"<<"ERROR::file model is not ready!";
    }
    return linNum;
}

int REngine::lastCharIndex(QString fileName) {
    int charIndex = KInvalidId;
    RFileModel* model = fileModel(fileName);
    if (model) {
        QString sql;
        sql.append(KSelect + KFileLastReadCharIndex
                   +KFrom + KTableFile
                   +KWhere + KFileId + KEqual +QString::number(model->fileId()));
        QSqlQuery *sqlQuery = query(sql);
        if (sqlQuery) {
            if (sqlQuery->next()) {
                charIndex = sqlQuery->value(sqlQuery->record().indexOf(KFileLastReadCharIndex)).toInt();
            }
            delete sqlQuery;
        }
    }
    else {
        qDebug()<<"REngine::lastCharIndex()"<<"ERROR::file model is not ready!";
    }
    return charIndex;
}

void REngine::saveSettings() {
    QSettings setting(KSettingOrganization, KSettingApplication);
    for (int i = 0; settingsMap[i][0].isValid(); i++ ) {
        setting.setValue(settingsMap[i][1].toString(), m_settings[settingsMap[i][1].toString()]);
    }
    setting.sync();
    qDebug()<<"saveSettings status"<<setting.status();
}

void REngine::saveSetting(const QString &setting, const QVariant& value) {
    QSettings settings(KSettingOrganization, KSettingApplication);
    settings.setValue(setting, value);
    settings.sync();
    qDebug()<<"saveSetting"<<setting<<"value"<<value<<"status"<<settings.status();
}

void REngine::loadSettings() {
    QSettings setting(KSettingOrganization, KSettingApplication);
    for (int i = 0; settingsMap[i][0].isValid(); i++ ) {
        m_settings[settingsMap[i][1].toString()] = adjustValueType((REngine::Settings)settingsMap[i][0].toInt(),
                                                                   setting.value(settingsMap[i][1].toString(),
                                                                                 settingsMap[i][2]));
        qDebug()<<"load setting"<<settingsMap[i][1].toString()<<m_settings[settingsMap[i][1].toString()];
    }
}

QVariant REngine::setting(REngine::Settings id) {
    for (int i = 0; settingsMap[i][0].isValid(); i++) {
        if (settingsMap[i][0] == QVariant(id)) {
            qDebug()<<"get setting:"<<settingsMap[i][1].toString()
                   <<"value:"<<m_settings[settingsMap[i][1].toString()];
            if (id == TextColor && setting(NightMode).toBool()) {
                qDebug()<<"Night mode, use night font color:"<<KNightModeFontColor;
                return QVariant(KNightModeFontColor);
            }
            else if (id == TextBackgroud && setting(NightMode).toBool()) {
                qDebug()<<"Night mode, use night background color:"<<KNightModeBackgroundColor;
                return QVariant(KNightModeBackgroundColor);
            }
            else {
                return m_settings[settingsMap[i][1].toString()];
            }
        }
    }
    return QVariant();
}

void REngine::setSetting(REngine::Settings id, QVariant value) {
    QVariant adjustedValue = adjustValueType(id, value);
    qDebug()<<"setSetting, id"<<id<<"value"<<value<<"adjusted"<<adjustedValue;
    for (int i = 0; settingsMap[i][0].isValid(); i++ ) {
        if (settingsMap[i][0] == QVariant(id)) {
            m_settings[settingsMap[i][1].toString()] = adjustedValue;
            qDebug()<<"setSetting"<<settingsMap[i][1].toString()
                   <<adjustedValue;
            saveSetting(settingsMap[i][1].toString(), adjustedValue);
            //If codec is changed, we need to reload the model
            if (id == REngine::Codec) {
                reloadFileModelForCodec();
            }
            else if (id == REngine::DisableDisplayBlank) {
                setDisableDisplayBlank(adjustedValue.toBool());
            }
            return;
        }
    }
}

void REngine::reloadAllFileModels() {
    for (QMap<QString, RFileModel*>::iterator i = m_fileName_model_map.begin();
         i != m_fileName_model_map.end(); i++) {
        i.value()->reset();
    }
}

void REngine::reloadFileModelForCodec() {
    QList<RFileInfo> infoList;
    //Get file info from db
    if (recentFileInfo(infoList)) {
        for (int i = 0; i < infoList.length(); i++) {
            //If codec is auto and model is exist, reset the model
            if (infoList[i].codec == KCodecAuto) {
                if (m_fileName_model_map.contains(infoList[i].name)) {
                    m_fileName_model_map[infoList[i].name]->reset();
                }
            }
        }
    }
}

REngine::Settings REngine::settingStrToId(const QString & settingStr) {
    qDebug()<<"settingStrToId, settingStr"<<settingStr;
    for (int i = 0; settingsMap[i][0].isValid(); i++ ) {
        if (settingsMap[i][1] == settingStr) {
            qDebug()<<"setting id"<<settingsMap[i][1].toInt();
            return (REngine::Settings)settingsMap[i][0].toInt();
        }
    }
    return (REngine::Settings)0;
}

QVariant REngine::adjustValueType(REngine::Settings id, QVariant value) {
    QVariant ret;
    for (int i = 0; settingsMap[i][0].isValid(); i++ ) {
        if (settingsMap[i][0] == QVariant(id)) {
            if (settingsMap[i][3] == KTypeInt) {
                ret = value.toInt();
            }
            else if (settingsMap[i][3] == KTypeStr) {
                ret = value.toString();
            }
            else if (settingsMap[i][3] == KTypeFloat) {
                ret = value.toFloat();
            }
            else if (settingsMap[i][3] == KTypeBool) {
                ret = value.toBool();
            }
        }
    }
    return ret;
}

void REngine::decodeFileName(QString fileName) {
    QTextDocument doc;
    doc.setHtml(fileName);
    qDebug()<<"REngine::decodeFileName"<<fileName<<doc.toPlainText();
    QFile file("/home/user/MyDocs/Documents/log.txt");
    file.open(QIODevice::WriteOnly);
    file.write(fileName.toLocal8Bit());
    file.flush();
}

int REngine::fileSize(QString fileName) {
    QFileInfo info(fileName);
    return info.size();
}

int REngine::lineNumberOfPercent(QString fileName, float percent) {
    RFileModel* model = fileModel(fileName);
    if (model) {
        return model->lineNumberOfPercent(percent);
    }
    else {
        qDebug()<<"REngine::lineNumberOfPercent()"<<"ERROR::file model is not ready!";
        return 0;
    }
}

float REngine::lineNumberToPercent(QString fileName, int lineNum) {
    RFileModel* model = fileModel(fileName);
    if (model) {
        return model->lineNumberToPercent(lineNum);
    }
    else {
        qDebug()<<"REngine::lineNumberToPercent()"<<"ERROR::file model is not ready!";
        return 0;
    }
}

QString REngine::epocToString(qint64 epocTime) {
    QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(epocTime);
    return dateTime.toString("yyyy-MMMM-dd hh:mm");
}

QVariant REngine::availableCodecs(bool addAuto) {
    QList<QByteArray> codecs = QTextCodec::availableCodecs();
    QStringList strCodecs;
    if (addAuto) {
        strCodecs.append(KCodecAuto);
    }
    for (int i = 0; i < codecs.count(); i++) {
        if (QString(codecs[i]) != KCodecSystem) {
            strCodecs.append(codecs[i]);
        }
    }
    return QVariant::fromValue(strCodecs);
}

#if USE_ENCA
QTextCodec *REngine::codecForString(QByteArray &stringArray, bool& useDefault) {
      EncaAnalyser analyser;
      EncaEncoding encoding;
      QTextCodec* codec(NULL);
      useDefault = false;
      //use real language key
      analyser = enca_analyser_alloc(systemLanguageCode().toLatin1().constData());
      if (analyser) {
          encoding = enca_analyse(analyser, (unsigned char*)stringArray.data(), stringArray.length());
          const char* codecName = enca_charset_name(encoding.charset,
                                                    ENCA_NAME_STYLE_ENCA);
          qDebug() << "Codec for str is: "<<codecName;
          QString qtCodec = enca2QtCodec(codecName);
          if (qtCodec != KCodecNotAvailable) {
            codec = QTextCodec::codecForName(qtCodec.toLatin1());
          }
          else {
              QString settingCodec = setting(REngine::Codec).toString();
              if (settingCodec == KCodecAuto) {
                  codec = QTextCodec::codecForName(defaultCodec().toLatin1());
              }
              else {
                  codec = QTextCodec::codecForName(settingCodec.toLatin1());
              }
              useDefault = true;
          }
          enca_analyser_free(analyser);
      }
      return codec;
}
#else
QTextCodec *REngine::codecForString(QByteArray &stringArray) {
    //convert input string to same

    size_t bufferSize = stringArray.size()*4;
    //alloc output string
    char* convIn = stringArray.data();
    char* outStr = new char[bufferSize];
    char* convOut = outStr;
    QTextCodec* codec = NULL;
    QByteArray convertedStr;

    QList<QByteArray> codecList = QTextCodec::availableCodecs();
    for (int i = 0; i < codecList.length(); i++) {
        memset(outStr, 0, bufferSize);

        //Use iconv to convert string
        iconv_t cd = iconv_open(codecList[i].toUpper(), codecList[i].toUpper());
        if ((int) cd == -1) {
            /* Initialization failure. */
            qDebug()<<"ERROR::Can not open codec:"<<codecList[i].toUpper();
            continue;
        }
        size_t inputSize = stringArray.size();
        size_t outputSize = bufferSize;
        size_t ret = iconv(cd, &convIn, &inputSize, &convOut, &outputSize);
        iconv_close(cd);
        convertedStr.clear();
        convertedStr.append(outStr, bufferSize-outputSize);
        if (ret == (size_t) - 1) {
            //Convert failed
            qDebug()<<"Can not be converted from:"<<codecList[i].toUpper();
        }
        else {
            qDebug()<<"Can be coverted from:"<<codecList[i].toUpper();
            codec = QTextCodec::codecForName(codecList[i]);
            break;
        }
    }
    delete outStr;
    return codec;
}
#endif

QString REngine::fileCodec(QString fileName) {
    QString sql, codec(KCodecNotAvailable);
    sql.append(KSelect +KFileCodec
               +KFrom + KTableFile + KWhere + KFileName + KEqual + "'" + fileName + "'");

    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"file data size"<<sqlQuery->size();
        while (sqlQuery->next()) {
            codec = sqlQuery->value(sqlQuery->record().indexOf(KFileCodec)).toString();
        }

        delete sqlQuery;
        sqlQuery = NULL;
    }
    return codec;
}

QString REngine::fileCurrentCodec(QString fileName) {
    RFileModel* model = fileModel(fileName);
    if (model) {
        return model->fileCodec();
    }
    else {
        return KCodecNotAvailable;
    }
}

void REngine::saveFileCodec(QString fileName, QString codec) {
    QString sql;
    RFileModel* model = fileModel(fileName);
    if (model) {
        int id = model->fileId();
        if (id != KInvalidId) {
            InsertUpdateHelper *helper = new InsertUpdateHelper(KTableFile);
            helper->setId(KFileId, id);
            helper->addValue(KFileCodec, codec);
            helper->updateSql(sql);

            QSqlQuery *sqlquery = query(sql, false);
            if (sqlquery) {
                qDebug()<<"REngine::saveFileCodec()"<<
                          "save file code success"<<fileName<<codec;
                if (model->fileCodec() != codec) {
                    model->reset();
                }
                delete sqlquery;
            }
            else {
                qDebug()<<"REngine::saveFileCodec()"<<"ERROR::save file codec failed"
                       <<fileName<<codec;
            }
            delete helper;
        }
    }
    else {
        qDebug()<<"REngine::saveFileCodec()"<<"ERROR::file model is not ready!";
    }
}

int REngine::saveBookmark(QString fileName, int lineNum,
                          int charIndex, QString note) {
    int id = fileId(fileName);
    int bookmarkId = KInvalidId;
    if (id == KInvalidId) {
        qDebug()<<"ERROR::fileId of "<<fileName<<"is not avaliable";
        return id;
    }

    QString sql;
    InsertUpdateHelper *helper = new InsertUpdateHelper(KTableBookmark);
    helper->setId(KBookmarkId, KInvalidId);
    helper->addValue(KBookmarkFileId, id);
    helper->addValue(KBookmarkLine, lineNum);
    helper->addValue(KBookmarkCharIndex, charIndex);
    helper->addValue(KBookmarkAddTime, QDateTime::currentMSecsSinceEpoch());
    helper->addValue(KBookmarkNote, note);
    helper->insertSql(sql);

    QSqlQuery *sqlquery = query(sql, true);
    if (sqlquery) {
        bookmarkId = sqlquery->lastInsertId().toInt();
        qDebug()<<"add bookmark "<<fileName<<
                  lineNum<<charIndex<<note<<"success, id:"<<bookmarkId;
        delete sqlquery;
    }
    else {
        qDebug()<<"ERROR::add file "<<fileName<<
                  lineNum<<charIndex<<note<<" to db failed";
    }
    delete helper;
    return bookmarkId;
}

QList<QObject*> REngine::bookmarks(QString fileName) {
    QString sql;
    RBookmark *mark;
    QList<QObject*> list;
    sql.append(KSelect +KTableBookmark+KDot+KBookmarkId+KAs+KBookmarkId + KComma + KBookmarkLine + KComma+KBookmarkNote
               + KComma + KBookmarkCharIndex + KComma +KBookmarkAddTime
               +KFrom + KTableBookmark + KJoin + KTableFile +KOn +
               KTableFile+KDot+KFileId +KEqual + KTableBookmark+KDot+KBookmarkFileId
               + KWhere + KTableFile + KDot + KFileName + KEqual + "'" + fileName + "'");

    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"bookmarks size"<<sqlQuery->size();
        while (sqlQuery->next()) {
            mark = new RBookmark();
            mark->setBookmarkId(sqlQuery->value(sqlQuery->record().indexOf(KBookmarkId)).toInt());
            mark->setLineNum(sqlQuery->value(sqlQuery->record().indexOf(KBookmarkLine)).toInt());
            mark->setCharIndex(sqlQuery->value(sqlQuery->record().indexOf(KBookmarkCharIndex)).toInt());
            mark->setAddTime(sqlQuery->value(sqlQuery->record().indexOf(KBookmarkAddTime)).toLongLong());;
            mark->setNote(sqlQuery->value(sqlQuery->record().indexOf(KBookmarkNote)).toString());
            qDebug()<<"id"<<mark->bookmarkId()
                   <<"lineNumber"<<mark->lineNum()
                  <<"charIndex"<<mark->charIndex()
                 <<"addTime"<<mark->addTime()
                <<"note"<<mark->note();
            list.append(mark);
        }

        delete sqlQuery;
        sqlQuery = NULL;
    }
    return list;
}


bool REngine::removeBookmark(int bookmarkId) {
    QString sql;
    bool result;
    sql.append(KDelete + KTableBookmark
               + KWhere + KBookmarkId + KEqual + QString::number(bookmarkId));

    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"bookmark deleted";
        result = true;
    }
    else {
        qDebug()<<"bookmark delete failed";
        result = false;
    }
    emit bookmarkChanged();
    return result;
}

bool REngine::removeAllBookmarks(QString fileName) {
    QString sql;
    bool result;
    sql.append(KDelete + KTableBookmark
               + KWhere + KBookmarkId + KIn
               + "(" + KSelect + KDistinct + KTableBookmark+KDot+KBookmarkId
               +KFrom + KTableBookmark + KJoin + KTableFile + KOn +
               KTableFile+KDot+KFileId +KEqual + KTableBookmark+KDot+KBookmarkFileId
               + KWhere + KTableFile+KDot+KFileName + KEqual + "'"+fileName+"'"+")");

    QSqlQuery *sqlQuery = query(sql);
    if (sqlQuery) {
        qDebug()<<"bookmarks deleted";
        result = true;
    }
    else {
        qDebug()<<"bookmarks delete failed";
        result = false;
    }
    emit bookmarkChanged();
    return result;
}

bool REngine::pathExist(QString path) {
    QDir dir(path);
    return dir.exists();
}

void REngine::copyToClipboard(QString text) {
    QApplication::clipboard()->setText(text);
}

QVariant REngine::availableCodecRegions() {

    QStringList regions;
    //Codec Auto is added by UI when necessary, no need to add it here
    //regions.append(KCodecAuto);
    regions.append(KLanRegionUnicode);
    regions.append(KLanRegionArabic);
    regions.append(KLanRegionBaltic);
    regions.append(KLanRegionCeltic);
    regions.append(KLanRegionCyrillic);
    regions.append(KLanRegionCentralEuro);
    regions.append(KLanRegionChinaMainLand);
    regions.append(KLanRegionChinaTaiwan);
    regions.append(KLanRegionChinaHongKong);
    regions.append(KLanRegionEasternEuro);
    regions.append(KLanRegionGreek);
    regions.append(KLanRegionHebrew);
    regions.append(KLanRegionJapanese);
    regions.append(KLanRegionKorean);
    regions.append(KLanRegionThai);
    regions.append(KLanRegionTurkish);
    regions.append(KLanRegionWesternEuro);
    regions.append(KLanRegionVietnamese);
    return QVariant::fromValue(regions);
}

QVariant REngine::availableCodecsForRegion(QString region) {

    QStringList strCodecs;
    if (region == KLanRegionUnicode) {
        strCodecs.append(KCodecUTF8);
        strCodecs.append(KCodecUTF16LE);
        strCodecs.append(KCodecUTF16BE);
        strCodecs.append(KCodecUTF32LE);
        strCodecs.append(KCodecUTF32BE);
    }
    else if (region == KLanRegionArabic) {
        strCodecs.append(KCodecISO_8859_6);
        strCodecs.append(KCodecWindows1256);
        strCodecs.append(KCodecWindows1257);
    }
    else if (region == KLanRegionBaltic) {
        strCodecs.append(KCodecISO_8859_4);
        strCodecs.append(KCodecISO_8859_13);
    }
    else if (region == KLanRegionCeltic) {
        strCodecs.append(KCodecISO_8859_14);
    }
    else if (region == KLanRegionCyrillic) {
        strCodecs.append(KCodecISO_8859_5);
        strCodecs.append(KCodecKOI8_U);
        strCodecs.append(KCodecKOI8_R);
        strCodecs.append(KCodecWindows1251);
    }
    else if (region == KLanRegionCentralEuro) {
        strCodecs.append(KCodecWindows1250);
    }
    else if (region == KLanRegionChinaMainLand) {
        strCodecs.append(KCodecGB2312);
        strCodecs.append(KCodecGB18030);
        strCodecs.append(KCodecGBK);
    }
    else if (region == KLanRegionChinaTaiwan) {
        strCodecs.append(KCodecBig5);
    }
    else if (region == KLanRegionChinaHongKong) {
        strCodecs.append(KCodecBig5_HKSCS);
    }
    else if (region == KLanRegionEasternEuro) {
        strCodecs.append(KCodecISO_8859_2);
    }
    else if (region == KLanRegionGreek) {
        strCodecs.append(KCodecISO_8859_7);
        strCodecs.append(KCodecWindows1253);
    }
    else if (region == KLanRegionHebrew) {
        strCodecs.append(KCodecISO_8859_8);
        strCodecs.append(KCodecWindows1255);
    }
    else if (region == KLanRegionJapanese) {
        strCodecs.append(KCodecShift_JIS);
        strCodecs.append(KCodecEUC_JP);
        strCodecs.append(KCodecISO_2022_JP);
    }
    else if (region == KLanRegionKorean) {
        strCodecs.append(KCodecEUC_KR);
        strCodecs.append(KCodecISO_2022_KR);
        strCodecs.append(KCodecWindows949);
    }
    else if (region == KLanRegionThai) {
        strCodecs.append(KCodecTIS_620);
    }
    else if (region == KLanRegionTurkish) {
        strCodecs.append(KCodecISO_8859_3);
        strCodecs.append(KCodecISO_8859_9);
        strCodecs.append(KCodecWindows1254);
    }
    else if (region == KLanRegionWesternEuro) {
        strCodecs.append(KCodecISO_8859_1);
        strCodecs.append(KCodecISO_8859_15);
        strCodecs.append(KCodecWindows1252);
    }
    else if (region == KLanRegionVietnamese) {
        strCodecs.append(KCodecWindows1258);
    }
    return QVariant::fromValue(strCodecs);
}

QString REngine::defaultCodec() {
    QString codec = KCodecUTF8;
    //Special treatment for chinese
    if (QLocale::system().language() == QLocale::Chinese) {
        switch (QLocale::system().country()) {
        case QLocale::Taiwan:
            codec = KLanRegionChinaTaiwanDefaultCodec;
            break;

        case QLocale::HongKong:
            codec = KLanRegionChinaHongKongDefaultCodec;
            break;

        case QLocale::China:
            codec = KLanRegionChinaMainLandDefaultCodec;
            break;

        default:
            codec = KCodecBig5;
            break;
        }
    }
    else {
        for (int i = 0; languageCodecMap[i][0].toInt() != 0; i++) {
            if ((int)QLocale::system().language() == languageCodecMap[i][0].toInt()) {
                codec = languageCodecMap[i][2].toString();
            }
        }
    }
    return codec;
}


QString REngine::systemLanguageCode() {
    QString code;
    for (int i = 0; languageCodecMap[i][0].toInt() != 0; i++) {
        if ((int)QLocale::system().language() == languageCodecMap[i][0].toInt()) {
            code = languageCodecMap[i][1].toString();
        }
    }
    qDebug()<<"systemLanguageCode::"<<code;
    return code;
}

QString REngine::enca2QtCodec(const QString &encaCodec) {
    QString qtCodec = KCodecNotAvailable;
    for (int i = 0; !enca2QtCodecMap[i][0].toString().isEmpty(); i++) {
        if (enca2QtCodecMap[i][0].toString() == encaCodec) {
            qtCodec = enca2QtCodecMap[i][1].toString();
            break;
        }
    }
    return qtCodec;
}

void REngine::chapterKeys(const QString& codec, QList<QString>& keys) {
    //Only support chinese now
    if (codec == KCodecGB2312 || codec == KCodecGB18030 || codec == KCodecGBK) {
        keys.append(KChapterKeyChineseSimple);
    }
    else if (codec == KCodecBig5 || codec == KCodecBig5_HKSCS) {
        keys.append(KChapterKeyChineseTraditional);
    }
    else if ((QLocale::system().language() == QLocale::Chinese) &&
             (codec == KCodecUTF8 || codec == KCodecUTF16LE ||
              codec == KCodecUTF16BE || codec==KCodecUTF32LE ||
              codec == KCodecUTF32BE)) {
        keys.append(KChapterKeyChineseSimple);
        keys.append(KChapterKeyChineseTraditional);
    }
    else {
        keys.append(KChapterKeyEnglish);
    }
}

QVariant REngine::chapters(REngine* engine, QString fileName) {
    RFileModel* model = engine->fileModel(fileName);
    QStringList charpters;
    if (model) {
        model->chapters(charpters);
        return QVariant::fromValue(charpters);
    }
    else {
        qDebug()<<"ERROR::getLine(): file model is not ready";
        return QVariant();
    }
}

void REngine::saveLastPostionToChapter(QString fileName, int charpterIndex) {
    RFileModel* model = fileModel(fileName);
    if (model) {
        int lineNumber = model->lineNumberOfChapter(charpterIndex);
        saveLastPosition(fileName, lineNumber, 0);
    }
    else {
        qDebug()<<"ERROR::getLine(): file model is not ready";
    }
}

bool REngine::showChapterMenu() {
    bool show = false;
    //Only show when is Chinese, do not ask me why, because I am Chinese!
    if (QLocale::system().language() == QLocale::Chinese) {
        show = true;
    }
    return show;
}

void REngine::asyncGetChapters(QString fileName) {
    QFuture<QVariant> future = QtConcurrent::run(REngine::chapters, this, fileName);
    m_get_chapter_watcher.setFuture(future);
}

int REngine::currentChapterIndex(QString fileName) {
    RFileModel* model = fileModel(fileName);
    int index = -1;
    if (model) {
        index = model->currentChapterIndex();
    }
    else {
        qDebug()<<"ERROR::getLine(): file model is not ready";
    }
    return index;
}

qint32 REngine::byteArrayToInt(QByteArray array, bool littleEndian) {
    //>= 4 bytes, to int32
    if (array.length() >=4 ) {
        if (littleEndian) {
            return qFromLittleEndian<qint32>((const uchar*)array.constData());
        }
        else {
            return qFromBigEndian<qint32>((const uchar*)array.constData());
        }
    }
    else if (array.length() >= 2) {
        //>=2 bytes and < 4 bytes, to int16
        return byteArrayToInt16(array, littleEndian);
    }
    else if (array.length() >= 1) {
        //only one byte, to char
        return byteArrayToChar(array);
    }
    else {
        //none, return -1
        return -1;
    }
}

quint32 REngine::byteArrayToUint(QByteArray array, bool littleEndian) {
    //>= 4 bytes, to int32
    if (array.length()>=4) {
        if (littleEndian) {
            return qFromLittleEndian<quint32>((const uchar*)array.constData());
        }
        else {
            return qFromBigEndian<quint32>((const uchar*)array.constData());
        }
    }
    else if (array.length()>=2) {
        //>=2 bytes and < 4 bytes, to uint16
        return byteArrayToUint16(array, littleEndian);
    }
    else if (array.length() >= 1) {
        //only one byte, to uchar
        return byteArrayToUchar(array);
    }
    else {
        //none, return -1
        return -1;
    }
}

qint16 REngine::byteArrayToInt16(QByteArray array, bool littleEndian) {
    if (littleEndian) {
        return qFromLittleEndian<qint16>((const uchar*)array.constData());
    }
    else {
        return qFromBigEndian<qint16>((const uchar*)array.constData());
    }
}

quint16 REngine::byteArrayToUint16(QByteArray array, bool littleEndian) {
    if (littleEndian) {
        return qFromLittleEndian<quint16>((const uchar*)array.constData());
    }
    else {
        return qFromBigEndian<quint16>((const uchar*)array.constData());
    }
}

char REngine::byteArrayToChar(QByteArray array) {
    return array[0];
}

uchar REngine::byteArrayToUchar(QByteArray array) {
    return (uchar)array[0];
}

QString REngine::uncompressedContentFileName(const QString& fileName) {
    QFileInfo fileInfo(fileName);
    QString path = QDir::cleanPath(fileInfo.absolutePath());
    path.replace(KPathSeparator, KUnderLine);
    path.replace(KPathSeparatorWin, KUnderLine);
    path.replace(KDriveSymbol, KUnderLine);
    path.append(fileInfo.fileName());
    path.append(KDot+KUncompressedFileSuffix);
    return QDir::cleanPath(storageLocation()+KPathSeparator+path);
}


bool REngine::setDisableDisplayBlank(bool enable) {
    qDebug()<<"setDisableDisplayBlank:" <<enable;
    if (enable) {
        displayBlankFeedTimeOut();
        m_display_blank_feed_timer.stop();
        m_display_blank_feed_timer.start(KDisableDisplayBlankFeedTimerInterval);
        disconnect(&m_display_blank_feed_timer, 0, 0, 0);
        connect(&m_display_blank_feed_timer, SIGNAL(timeout()), this, SLOT(displayBlankFeedTimeOut()));
    }
    else {
        m_display_blank_feed_timer.stop();
        disconnect(&m_display_blank_feed_timer, 0, 0, 0);
    }
    return true;
}


void REngine::displayBlankFeedTimeOut() {
    qDebug()<<"displayBlankFeedTimeOut";
    MeeGo::QmDisplayState displayState;
    displayState.setBlankingPause();
}

QVariant REngine::fontFamilies() {
    QFontDatabase fontDatabase;
    QStringList families = fontDatabase.families();
    return QVariant::fromValue(families);
}

